import { Sql, SQL_SYMBOLE } from '../sql';
import { DefaultInputObject, RowObject, WhereObject } from '../types';
import { XRowsets, Rowset } from '../rowset/rowset';
import { Field } from '../expression';

/**
 * 查询条件
 */
export abstract class Condition extends Sql {
  readonly $type: SQL_SYMBOLE.CONDITION = SQL_SYMBOLE.CONDITION;
  abstract readonly $kind: CONDITION_KIND;
  /**
   * and连接
   * @param condition 下一个查询条件
   * @returns 返回新的查询条件
   */
  and(condition: Condition): Condition {
    return ConditionBuilder.and(this, condition);

    // condition = ensureCondition(condition);
    // return new BinaryLogicCondition(LOGIC_OPERATOR.AND, this, SqlBuilder.group(condition));
  }

  /**
   * OR语句
   * @param condition
   * @returns 返回新的查询条件
   */
  or(condition: Condition): Condition {
    return ConditionBuilder.or(this, condition);
  }

  static isCondition(object: any): object is Condition {
    return object?.$type === SQL_SYMBOLE.CONDITION;
  }
}

/**
 * 逻辑运算符列表
 */
export enum LOGIC_OPERATOR {
  AND = 'AND',
  OR = 'OR',
  NOT = 'NOT',
}

/**
 * 比较运算符
 */
export type COMPARE_OPERATOR = UNARY_COMPARE_OPERATOR | BINARY_COMPARE_OPERATOR;
/**
 * 比较运算符列表
 */
export enum BINARY_COMPARE_OPERATOR {
  IN = 'IN',
  NOT_IN = 'NOT IN',
  EQ = '=',
  NEQ = '<>',
  GT = '>',
  GTE = '>=',
  LT = '<',
  LTE = '<=',
  LIKE = 'LIKE',
  NOT_LIKE = 'NOT LIKE',
}

export enum UNARY_COMPARE_OPERATOR {
  IS_NULL = 'IS NULL',
  IS_NOT_NULL = 'IS NOT NULL',
}

export enum CONDITION_KIND {
  GROUP = 'GROUP',
  UNARY_COMPARE = 'UNARY_COMPARE',
  BINARY_COMPARE = 'BINARY_COMPARE',
  BINARY_LOGIC = 'BINARY_LOGIC',
  UNARY_LOGIC = 'UNARY_LOGIC',
  EXISTS = 'EXISTS',
  STANDARD = 'STANDARD',
}

/**
 * 可兼容的查询条件
 */
export type XCondition<T extends RowObject = DefaultInputObject> =
  | Condition
  | WhereObject<T>;

export const ConditionBuilder = {
  group(condition: Condition) {
    return new GroupCondition(condition);
  },
  and(...conditions: [Condition[]] | [Condition, Condition, ...Condition[]]) {
    const conds: Condition[] = (
      Array.isArray(conditions[0]) ? conditions[0] : conditions
    ) as Condition[];
    assert(conds.length >= 2, `Conditions must more than 2 conditions.`);
    return join(LOGIC_OPERATOR.AND, conds);
  },
  or(...conditions: [Condition[]] | [Condition, Condition, ...Condition[]]) {
    const conds: Condition[] = (
      Array.isArray(conditions[0]) ? conditions[0] : conditions
    ) as Condition[];
    assert(conds.length >= 2, `Conditions must more than 2 conditions.`);
    return join(LOGIC_OPERATOR.OR, conds);
  },
  parse<T extends RowObject>(
    condition: WhereObject<T>,
    rowset?: XRowsets<T>
  ): Condition {
    let makeField: (name: string) => Field;
    if (rowset) {
      if (typeof rowset === 'string' || Array.isArray(rowset)) {
        makeField = (key: string) => new Field(key, rowset);
      } else if (Rowset.isRowset(rowset)) {
        makeField = (key: string) => rowset.$field(key as any);
      }
    } else {
      makeField = (key: string) => new Field(key);
    }

    const compares = Object.entries(condition).map(([key, value]) => {
      const field: Field<any, string> = makeField(key);
      if (value === null || value === undefined) {
        return field.isNull();
      }
      if (Array.isArray(value)) {
        return field.in(value);
      }
      return field.eq(value as any);
    });

    return compares.length >= 2 ? ConditionBuilder.and(compares) : compares[0];
  },
};

export type ConditionBuilder = typeof ConditionBuilder;

function join(
  logic: LOGIC_OPERATOR.AND | LOGIC_OPERATOR.OR,
  conditions: Condition[]
): Condition {
  if (conditions.length < 2) {
    throw new Error(`Conditions must more than or equals 2 element.`);
  }
  const cond: Condition = conditions.reduce((previous, current) => {
    let condition = current;
    // 如果是二元逻辑条件运算，则将其用括号括起来，避免逻辑运算出现优先级的问题
    if (BinaryLogicCondition.isBinaryLogicCondition(condition)) {
      condition = ConditionBuilder.group(condition);
    }
    if (!previous) return condition;
    return new BinaryLogicCondition(logic, previous, condition);
  }) as Condition;
  return ConditionBuilder.group(cond);
}

import { GroupCondition } from './group-condition';
import { assert } from 'console';
import { BinaryLogicCondition } from './binary-logic-condition';
